#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
bandit 插件
用于 Python 代码安全漏洞检查
"""

import os
import subprocess
import json
from typing import Dict, List, Any


class BanditPlugin:
    """bandit 插件"""
    
    def __init__(self):
        self.name = "bandit"
        self.description = "Python 代码安全漏洞检查"
    
    def execute(self, config: Dict[str, Any], context: Dict[str, Any]) -> Dict[str, Any]:
        """执行 bandit 安全检查"""
        try:
            # 检查是否有 Python 文件
            python_files = self._find_python_files(config.get('paths', ['.']))
            if not python_files:
                return {
                    'success': True,
                    'output': '未找到 Python 文件，跳过 bandit 检查',
                    'exit_code': 0
                }
            
            # 检查 bandit 是否安装
            if not self._check_bandit_installed():
                return {
                    'success': False,
                    'output': 'bandit 未安装，请运行: pip install bandit',
                    'exit_code': 1
                }
            
            # 获取配置参数
            confidence_level = config.get('confidence', 'LOW')  # LOW, MEDIUM, HIGH
            severity_level = config.get('severity', 'LOW')     # LOW, MEDIUM, HIGH
            exclude_paths = config.get('exclude', [])
            skip_tests = config.get('skip', [])
            config_file = config.get('config_file', None)
            
            # 构建命令
            cmd = ['bandit', '-r']
            
            # 添加置信度级别
            cmd.extend(['--confidence-level', confidence_level.lower()])
            
            # 添加严重性级别  
            cmd.extend(['--severity-level', severity_level.lower()])
            
            # 添加输出格式 (JSON)
            cmd.extend(['-f', 'json'])
            
            # 添加排除路径
            if exclude_paths:
                for exclude in exclude_paths:
                    cmd.extend(['--exclude', exclude])
            
            # 跳过特定测试
            if skip_tests:
                cmd.extend(['--skip', ','.join(skip_tests)])
            
            # 添加配置文件
            if config_file and os.path.exists(config_file):
                cmd.extend(['-c', config_file])
            
            # 添加要检查的路径
            paths = config.get('paths', ['.'])
            cmd.extend(paths)
            
            # 执行命令
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=300
            )
            
            # 解析 JSON 结果
            try:
                if result.stdout:
                    bandit_result = json.loads(result.stdout)
                else:
                    bandit_result = {'results': [], 'metrics': {}}
            except json.JSONDecodeError:
                return {
                    'success': False,
                    'output': f'bandit 输出解析失败:\n{result.stdout}\n{result.stderr}',
                    'exit_code': 1
                }
            
            # 分析结果
            results = bandit_result.get('results', [])
            metrics = bandit_result.get('metrics', {})
            
            # 按严重性分类
            high_issues = [r for r in results if r.get('issue_severity') == 'HIGH']
            medium_issues = [r for r in results if r.get('issue_severity') == 'MEDIUM']
            low_issues = [r for r in results if r.get('issue_severity') == 'LOW']
            
            total_issues = len(results)
            
            if total_issues == 0:
                return {
                    'success': True,
                    'output': 'bandit 安全检查通过，未发现安全问题',
                    'exit_code': 0,
                    'details': {
                        'total_issues': 0,
                        'high_severity': 0,
                        'medium_severity': 0,
                        'low_severity': 0,
                        'files_scanned': metrics.get('_totals', {}).get('files', 0),
                        'lines_scanned': metrics.get('_totals', {}).get('loc', 0)
                    }
                }
            else:
                # 生成详细报告
                output = f"bandit 发现 {total_issues} 个安全问题：\n"
                output += f"- 高危: {len(high_issues)}\n"
                output += f"- 中危: {len(medium_issues)}\n"
                output += f"- 低危: {len(low_issues)}\n\n"
                
                # 显示高危问题
                if high_issues:
                    output += "高危问题详情:\n"
                    for issue in high_issues[:5]:  # 只显示前5个
                        output += f"  {issue['filename']}:{issue['line_number']} - {issue['test_name']}\n"
                        output += f"    {issue['issue_text']}\n"
                    if len(high_issues) > 5:
                        output += f"  ... 还有 {len(high_issues) - 5} 个高危问题\n"
                    output += "\n"
                
                # 显示中危问题
                if medium_issues:
                    output += "中危问题详情:\n"
                    for issue in medium_issues[:3]:  # 只显示前3个
                        output += f"  {issue['filename']}:{issue['line_number']} - {issue['test_name']}\n"
                        output += f"    {issue['issue_text']}\n"
                    if len(medium_issues) > 3:
                        output += f"  ... 还有 {len(medium_issues) - 3} 个中危问题\n"
                    output += "\n"
                
                # 根据严重性决定是否失败
                should_fail = len(high_issues) > 0 or (config.get('fail_on_medium', False) and len(medium_issues) > 0)
                
                return {
                    'success': not should_fail,
                    'output': output,
                    'exit_code': 1 if should_fail else 0,
                    'details': {
                        'total_issues': total_issues,
                        'high_severity': len(high_issues),
                        'medium_severity': len(medium_issues),
                        'low_severity': len(low_issues),
                        'files_scanned': metrics.get('_totals', {}).get('files', 0),
                        'lines_scanned': metrics.get('_totals', {}).get('loc', 0),
                        'high_issues': high_issues,
                        'medium_issues': medium_issues,
                        'low_issues': low_issues
                    }
                }
                
        except subprocess.TimeoutExpired:
            return {
                'success': False,
                'output': 'bandit 检查超时',
                'exit_code': 124
            }
        except Exception as e:
            return {
                'success': False,
                'output': f'bandit 检查失败: {str(e)}',
                'exit_code': 1
            }
    
    def _find_python_files(self, paths: List[str]) -> List[str]:
        """查找 Python 文件"""
        python_files = []
        
        for path in paths:
            if os.path.isfile(path) and path.endswith('.py'):
                python_files.append(path)
            elif os.path.isdir(path):
                for root, dirs, files in os.walk(path):
                    # 跳过常见的忽略目录
                    dirs[:] = [d for d in dirs if d not in ['.git', '__pycache__', '.pytest_cache', 'venv', '.venv']]
                    
                    for file in files:
                        if file.endswith('.py'):
                            python_files.append(os.path.join(root, file))
        
        return python_files
    
    def _check_bandit_installed(self) -> bool:
        """检查 bandit 是否安装"""
        try:
            subprocess.run(['bandit', '--version'], capture_output=True, check=True)
            return True
        except (subprocess.CalledProcessError, FileNotFoundError):
            return False


def execute(config: Dict[str, Any], context: Dict[str, Any]) -> Dict[str, Any]:
    """插件入口函数"""
    plugin = BanditPlugin()
    return plugin.execute(config, context)
